//!
/**
 ***********************************************************************************************************
 * <RBD3D9Texture Implementation>
 ***********************************************************************************************************
 * @file RBD3D9Texture.cpp
 ***********************************************************************************************************/

/***********************************************************************************************************
 * Precompiled Header
 ***********************************************************************************************************/
#include "RBD3D9RendererPCH.h"

/***********************************************************************************************************
 * Includes
 ***********************************************************************************************************/
#include <RBMain/RBPixelFormat.h>

#include "RBD3D9Texture.h"
#include "RBD3D9PixelBuffer.h"
#include "RBD3D9Renderer.h"
#include "RBD3D9Tools.h"

/***********************************************************************************************************
 * Defines
 ***********************************************************************************************************/

/***********************************************************************************************************
 * Classes implementation
 ***********************************************************************************************************/

/**
 ***********************************************************************************************************
 * Constructor
 ***********************************************************************************************************/
RBD3D9Texture::RBD3D9Texture(const char* pcFilename, RBHardwareBufferManager* pCreator)
: RBTexture(RBTexture::HBU_DEFAULT, MP_GPU, pCreator)
, m_pD3DTexture(NULL)
{
    m_bIsSourceFile = true;
    m_strSourceFile = pcFilename;
    if (CreateFromFile())
    {
        CreatePixelBuffers(pCreator, MP_GPU);
        InitPixelBuffers();
    }
}
/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBD3D9Texture::RBD3D9Texture(uint32 uiTextureUsage, RBHardwareBufferManager* pCreator, uint32 uiWidth, uint32 uiHeight, 
                             RBPixelFormat eFormat, RBTextureType::EType eType, uint32 uiMips, uint32 uiDepth, uint32 eMemPool)
: RBTexture(uiTextureUsage, eMemPool, pCreator)
{
    m_bIsSourceFile = false;
    m_uiWidth = uiWidth;
    m_uiHeight = uiHeight;
    m_eType = eType;
    m_uiDepth = uiDepth;
    m_ePixelFormat = eFormat;
    m_uiFaceCount = (m_eType == RBTextureType::CUBE) ? 6 : 1;
    m_uiMipsAsked = uiMips;

    if (CreateFromScratch(m_uiMipsAsked))
    {
        CreatePixelBuffers(pCreator, m_uiMemPoolMask);
        InitPixelBuffers();
    }

}

/**
 ***********************************************************************************************************
 * Destructor
 ***********************************************************************************************************/
RBD3D9Texture::~RBD3D9Texture()
{
    RBCommon::SafeRelease(m_pD3DTexture);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D9Texture::CreatePixelBuffers(RBHardwareBufferManager* pCreator, uint32 uiMemPoolMask)
{
    m_apPixelBuffers = RBNew RBPixelBufferPtr[m_uiFaceCount*m_uiMipCount];
    for (uint32 uiFace=0; uiFace<m_uiFaceCount; ++uiFace)
    {
        for (uint32 uiMip=0; uiMip<m_uiMipCount; ++uiMip)
        {
            m_apPixelBuffers[GetBufferIndex(uiMip, uiFace)] = 
                RBNew RBD3D9PixelBuffer(m_uiUsage, uiMemPoolMask, pCreator, m_uiWidth, m_uiHeight, m_ePixelFormat, 0, this, false);
        }
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
void RBD3D9Texture::OnReset(bool bBeforeReset)
{
    Super::OnReset(bBeforeReset);

    if (bBeforeReset)
    {
        for (uint32 uiFace=0; uiFace<m_uiFaceCount; ++uiFace)
        {
            for (uint32 uiMip=0; uiMip<m_uiMipCount; ++uiMip)
            {
                m_apPixelBuffers[GetBufferIndex(uiMip, uiFace)]->OnReset(bBeforeReset);
            }
        }
        RBCommon::SafeRelease(m_pD3DTexture);
        m_pD3DTexture = 0;
    }
    else
    {
        if (m_bIsSourceFile)
        {
            CreateFromFile();
        }
        else
        {
            CreateFromScratch(m_uiMipsAsked);
        }
        InitPixelBuffers();
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
void RBD3D9Texture::InitPixelBuffers()
{
    for (uint32 uiFace=0; uiFace<m_uiFaceCount; ++uiFace)
    {
        for (uint32 uiMip=0; uiMip<m_uiMipCount; ++uiMip)
        {
            IDirect3DSurface9* pSurface = NULL;
            if (m_eType == RBTextureType::CUBE)
            {
                static_cast<IDirect3DCubeTexture9*>(m_pD3DTexture)->GetCubeMapSurface((D3DCUBEMAP_FACES)uiFace, uiMip, &pSurface);
            }
            else
            {
                static_cast<IDirect3DTexture9*>(m_pD3DTexture)->GetSurfaceLevel(uiMip, &pSurface);
            }

            if (pSurface)
            {
                RBSmartPointerCast(RBD3D9PixelBuffer, m_apPixelBuffers[GetBufferIndex(uiMip, uiFace)])->SetSurface(pSurface);
            }
            pSurface->Release();
        }
    }
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
bool RBD3D9Texture::CreateFromFile()
{
    bool bAllocatePixelBuffers = false;
    D3DXIMAGE_INFO info;
    if (SUCCEEDED(D3DXGetImageInfoFromFile(m_strSourceFile.c_str(), &info)))
    {
        bAllocatePixelBuffers = false;
        if (info.ResourceType == D3DRTYPE_CUBETEXTURE)
        {
            IDirect3DCubeTexture9* pCube = NULL;
            D3DXCreateCubeTextureFromFile(RBD3D9Renderer::GetSingleton()->GetD3DDevice(), m_strSourceFile.c_str(), &pCube);
            m_pD3DTexture = pCube;
            m_eType = RBTextureType::CUBE;
            m_uiFaceCount = 6;
            bAllocatePixelBuffers = true;
        }
        else if (info.ResourceType == D3DRTYPE_VOLUMETEXTURE)
        {
            IDirect3DVolumeTexture9* pVolume = NULL;
            D3DXCreateVolumeTextureFromFile(RBD3D9Renderer::GetSingleton()->GetD3DDevice(), m_strSourceFile.c_str(), &pVolume);
            m_pD3DTexture = pVolume;
            m_eType = RBTextureType::VOLUME;
            m_uiFaceCount = 1;
            m_apPixelBuffers = NULL;
        }
        else // Assume it's a regular texture
        {
            IDirect3DTexture9* pTexture = NULL;
            D3DXCreateTextureFromFile(RBD3D9Renderer::GetSingleton()->GetD3DDevice(), m_strSourceFile.c_str(), &pTexture);
            m_pD3DTexture = pTexture;
            m_eType = RBTextureType::SURFACE;
            bAllocatePixelBuffers = true;
        }
        m_uiWidth = info.Width;
        m_uiHeight = info.Height;
        m_uiDepth = info.Depth;
        m_uiMipCount = m_pD3DTexture->GetLevelCount();
    }
    return bAllocatePixelBuffers;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
bool RBD3D9Texture::CreateFromScratch(uint32 uiMips)
{
    uint32 d3dUsage = 0;

    if ((m_uiTextureUsage & HBU_CPUREAD) ||(m_uiTextureUsage & HBU_CPUWRITE))
    {
        d3dUsage |= D3DUSAGE_DYNAMIC;
    }

    if (m_uiTextureUsage & HBU_GPUWRITE)
    {
        d3dUsage |= D3DUSAGE_RENDERTARGET;
    }

    if (m_uiTextureUsage & HBU_DEPTHSTENCIL)
    {
        d3dUsage |= D3DUSAGE_DEPTHSTENCIL;
    }

    D3DPOOL d3dPool = RBD3D9Tools::ComputeD3DPool((RBHardwareBuffer::MemPool)m_uiMemPoolMask);

    switch (m_eType)
    {
    case RBTextureType::SURFACE:
        {
            IDirect3DTexture9* pTexture;
            RBD3D9Renderer::GetSingleton()->GetD3DDevice()->CreateTexture(m_uiWidth, m_uiHeight, uiMips, d3dUsage, RBD3D9Tools::ComputeD3DFormat(m_ePixelFormat), d3dPool, &pTexture, NULL);
            m_pD3DTexture = pTexture;
            break;
        }
    case RBTextureType::VOLUME:
        {
            IDirect3DVolumeTexture9* pVolume;
            RBD3D9Renderer::GetSingleton()->GetD3DDevice()->CreateVolumeTexture(m_uiWidth, m_uiHeight, m_uiDepth, uiMips, d3dUsage, RBD3D9Tools::ComputeD3DFormat(m_ePixelFormat), d3dPool, &pVolume, NULL);
            m_pD3DTexture = pVolume;
            break;
        }
    case RBTextureType::CUBE:
        {
            IDirect3DCubeTexture9* pCube;
            RBD3D9Renderer::GetSingleton()->GetD3DDevice()->CreateCubeTexture(m_uiWidth, uiMips, d3dUsage, RBD3D9Tools::ComputeD3DFormat(m_ePixelFormat), d3dPool, &pCube, NULL);
            m_pD3DTexture = pCube;
            break;
        }
    default:
        break;
    }

    m_uiMipCount = m_pD3DTexture->GetLevelCount();

    return true;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
void RBD3D9Texture::Resize(uint32 uiWidth, uint32 uiHeight)
{
    Super::Resize(uiWidth, uiHeight);
}
/***********************************************************************************************************/
